// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import "./interfaces/IPool.sol";
import {IACLManager} from "./interfaces/IACLManager.sol";
import {IInvestmentManager} from "./interfaces/IInvestmentManager.sol";

contract Pool is IPool, ReentrancyGuard {
    using SafeERC20 for IERC20;

    Status public override currentStatus;
    address public override ACLManager;
    address public override baseToken;
    address public override investmentManager;
    // user -> deposit amount
    mapping(address => uint256) public override deposits;
    uint256 public override depositsAmount;
    uint256 public override returnedAmount;

    constructor(address newAclManager, address newBaseToken, address newInvestmentManager) {
        currentStatus = Status.FUNDRAISING;
        ACLManager = newAclManager;
        baseToken = newBaseToken;
        investmentManager = newInvestmentManager;
    }

    function deposit(uint256 amount) external override nonReentrant {
        require(currentStatus == Status.FUNDRAISING, "Pool: deposit: current status is not FUNDRAISING");
        require(amount != 0, "Pool: deposit: amount is zero");

        uint256 balanceBefore = IERC20(baseToken).balanceOf(address(this));
        IERC20(baseToken).safeTransferFrom(msg.sender, address(this), amount);
        uint256 balanceAfter = IERC20(baseToken).balanceOf(address(this));
        uint256 realAmount = balanceAfter - balanceBefore;
        require(realAmount == amount, "Pool: deposit: fee tokens are not supported");

        deposits[msg.sender] += amount;
        depositsAmount += amount;
        emit Deposit(msg.sender, amount);
    }

    function withdraw() external override {
        require(currentStatus == Status.FINISHED, "Pool: withdraw: current status is not FINISHED");

        uint256 userShare = deposits[msg.sender];
        require(userShare != 0, "Pool: withdraw: user deposit is zero");

        uint256 withdrawAmount = (returnedAmount * userShare) / depositsAmount;
        require(withdrawAmount != 0, "Pool: withdraw: user withdrawAmount is zero");

        IERC20(baseToken).safeTransfer(msg.sender, withdrawAmount);
    }

    function launchPool() external override onlyProtocolTrader {
        require(currentStatus == Status.FUNDRAISING, "Pool: launchPool: current status is not FUNDRAISING");
        currentStatus = Status.LAUNCHED;
        IERC20(baseToken).safeApprove(investmentManager, depositsAmount);
        IInvestmentManager(investmentManager).entry();
    }

    function finishPool() external override onlyProtocolTrader {
        require(currentStatus == Status.LAUNCHED, "Pool: finishPool: current status is not LAUNCHED");
        currentStatus = Status.FINISHED;
        IInvestmentManager(investmentManager).exit();
        returnedAmount = IERC20(baseToken).balanceOf(address(this));
    }

    modifier onlyProtocolAdmin() {
        require(IACLManager(ACLManager).isProtocolAdmin(msg.sender), "Pool: caller is not protocol admin");
        _;
    }

    modifier onlyProtocolTrader() {
        require(IACLManager(ACLManager).isProtocolTrader(msg.sender), "Pool: caller is not protocol trader");
        _;
    }
}
